home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / mush-7.1.1 / pick.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-05-02  |  16.6 KB  |  619 lines

  1. /* @(#)pick.c    2.4    (c) copyright 10/18/86 (Dan Heller) */
  2.  
  3. #include "mush.h"
  4.  
  5. static int before, after, search_from, search_subj, search_to, xflg, icase;
  6. static char search_hdr[64];
  7. static int mdy[3];
  8. static int pick();
  9. static void month_day_year();
  10.  
  11. do_pick(n, argv, list)
  12. register int n;
  13. register char **argv, list[];
  14. {
  15.     char ret_list[MAXMSGS_BITS];
  16.  
  17.     if (n > 1 && !strcmp(argv[1], "-?"))
  18.     return help(0, "pick", cmd_help);
  19.  
  20.     clear_msg_list(ret_list);
  21.     /* if is_pipe, then the messages to search for are already set.
  22.      * if not piped, then reverse the bits for all message numbers.
  23.      * That is, search EACH message. only those matching will be returned.
  24.      */
  25.     if (isoff(glob_flags, IS_PIPE))
  26.     bitput(ret_list, list, msg_cnt, =~); /* macro, turn on all bits */
  27.     /* Use n temporarily as verbosity flag */
  28.     n = (!chk_option("quiet", "pick") &&
  29.         isoff(glob_flags, DO_PIPE));
  30.     if ((n = pick(argv, list, ret_list, n)) == -1)
  31.     return -1;
  32.     if (istool && isoff(glob_flags, DO_PIPE))
  33.     print("%d matches:\n", n);
  34.     for (n = 0; n < msg_cnt; n++)
  35.     if (msg_bit(ret_list, n)) {
  36.         if (isoff(glob_flags, DO_PIPE))
  37.         if (istool)
  38.             print_more("%d ", n+1);
  39.         else
  40.             print("%s\n", compose_hdr(n));
  41.         set_msg_bit(list, n);
  42.     } else
  43.         unset_msg_bit(list, n);
  44.     return 0;
  45. }
  46.  
  47. /*
  48.  * search for messages.  Return the number of matches.  Errors such
  49.  * as internal errors or syntax errors, return -1.
  50.  * "head" and "tail" are specified using +<num> or -<num> as args.
  51.  * Both can be specified and the order is significant.
  52.  *    pick +5 -3
  53.  * returns the last three of the first five matches.
  54.  *    pick -3 +2
  55.  * returns the first two of the last three matches.
  56.  */
  57. static int
  58. pick(argv, list, ret_list, verbose)
  59. register char **argv, list[], ret_list[];
  60. {
  61.     register char c;
  62.     int matches = 0;
  63.     char pattern[256];
  64.     short head_first, head_cnt, tail_cnt, search = TRUE;
  65.     int n;
  66.  
  67.     if (!msg_cnt) {
  68.     print("No Messages.\n");
  69.     return -1;
  70.     }
  71.  
  72.     head_first = TRUE;
  73.     head_cnt = tail_cnt = -1;
  74.     icase = before = after = search_from = search_subj = search_to = xflg = 0;
  75.     mdy[0] = mdy[1] = search_hdr[0] = 0;
  76.     while (*argv && *++argv && (**argv == '-' || **argv == '+'))
  77.     if (**argv == '+' || isdigit(argv[0][1])) {
  78.         if (**argv == '+')
  79.         head_cnt = atoi(&argv[0][1]);
  80.         else {
  81.         tail_cnt = atoi(&argv[0][1]);
  82.         if (head_cnt == -1)
  83.             head_first = FALSE;
  84.         }
  85.         if (head_cnt == 0 || tail_cnt == 0) {
  86.         print("pick: invalid head/tail number: %s\n", &argv[0][1]);
  87.         clear_msg_list(ret_list);
  88.         return -1;
  89.         }
  90.     } else if ((c = argv[0][1]) == 'e') {
  91.         if (!*++argv) {
  92.         print("use: -e expression...\n");
  93.         return -1;
  94.         }
  95.         break;
  96.     } else switch (c) {
  97.         /* users specifies a range */
  98.         case 'r': {
  99.         int X = 2;
  100.         /* if not a pipe, then clear all bits cuz we only want
  101.          * to search the message specified here...
  102.          * If it is a pipe, then add to the messages searched for.
  103.          */
  104.         if (isoff(glob_flags, IS_PIPE))
  105.             clear_msg_list(list);
  106.         /*  "-r10-15"
  107.          *     ^argv[1][2]  if NULL, then
  108.          * list detached from "r" e.g. "-r" "5-20"
  109.          */
  110.         if (!argv[0][X])
  111.             argv++, X = 0;
  112.         (*argv) += X;
  113.         n = get_msg_list(argv, list);
  114.         (*argv) -= X;
  115.         if (n == -1)
  116.             return -1;
  117.         argv += (n-1); /* we're going to increment another up top */
  118.         }
  119.         when 'a': {
  120.         if ((n = ago_date(++argv)) == -1)
  121.             return -1;
  122.         argv += n;
  123.         }
  124.         when 'd':
  125.         if (!*++argv) {
  126.             print("Specify a date for -%c\n", c);
  127.             return -1;
  128.         }
  129.         if (!date1(*argv))
  130.             return -1;
  131.         when 's' : case 'f': case 't': case 'h':
  132.         if (search_subj + search_from + search_to + *search_hdr > 1) {
  133.             print("Specify one of `s', `f', `t' or `h' only\n");
  134.             return -1;
  135.             }
  136.             if (c == 's')
  137.             search_subj = 1;
  138.         else if (c == 'f')
  139.             search_from = 1;
  140.         else if (c == 'h')
  141.             if (!*++argv)
  142.             print("Specify header to search for.\n");
  143.             else
  144.             (void) lcase_strcpy(search_hdr, *argv);
  145.         else
  146.             search_to = 1;
  147.         when 'x' : xflg = 1;
  148.         when 'i' : icase = 1;
  149.         otherwise:
  150.         print("pick: unknown flag: %c\n", argv[0][1]);
  151.         clear_msg_list(ret_list);
  152.         return -1;
  153.     }
  154.     if (xflg && head_cnt + tail_cnt >= 0) {
  155.     print("Can't specify -x and head/tail options together.\n");
  156.     return -1;
  157.     }
  158.     pattern[0] = 0;
  159.     (void) argv_to_string(pattern, argv);
  160.     search = (pattern[0] || head_cnt + tail_cnt < 0);
  161.     if (verbose) {
  162.     if (head_cnt + tail_cnt >= 0) {
  163.         print("Finding the ");
  164.         if (head_cnt > 0) {
  165.         if (head_first)
  166.             if (tail_cnt == -1)
  167.             print_more("first %d message%s",
  168.                 head_cnt, head_cnt > 1? "s" : "");
  169.             else
  170.             print_more("last %d message%s",
  171.                 tail_cnt, tail_cnt > 1? "s" : "");
  172.         else /* there must be a tail_cnt and it comes first */
  173.             print_more("first %d message%s",
  174.                 head_cnt, head_cnt > 1? "s" : "");
  175.         } else
  176.         print_more("last %d message%s",
  177.             tail_cnt, tail_cnt > 1? "s" : "");
  178.         if (tail_cnt > 0 && head_cnt > 0)
  179.         if (head_first)
  180.             print_more(" of the first %d", head_cnt);
  181.         else
  182.             print_more(" of the last %d", tail_cnt);
  183.     } else
  184.         print_more("Searching for messages");
  185.     if (!search) {
  186.         if (tail_cnt > 0 && head_cnt > 0)
  187.         print_more(" messages");
  188.         if (ison(glob_flags, IS_PIPE))
  189.         print_more(" from the input list");
  190.     } else if (mdy[1] == 0) {
  191.         print(" that %scontain \"%s\"", (xflg)? "do not ": "",
  192.                 (*pattern)? pattern: "<previous expression>");
  193.         if (search_subj)
  194.         print_more(" in subject line");
  195.         else if (search_from)
  196.         print_more(" from author names");
  197.         else if (search_to)
  198.         print_more(" from the To: field");
  199.         else if (search_hdr[0])
  200.         print_more(" from the message header \"%s:\"", search_hdr);
  201.     } else {
  202.         extern char *month_names[]; /* from dates.c */
  203.         print_more(" dated ");
  204.         if (before || after)
  205.         if (xflg)
  206.             print_more("%s ", (!before)? "before": "after");
  207.         else
  208.             print_more("on or %s ", (before)? "before": "after");
  209.         print_more("%s. %d, %d",
  210.               month_names[mdy[0]], mdy[1], mdy[2] + 1900);
  211.     }
  212.     print_more(".\n");
  213.     }
  214.     if (mdy[1] > 0 && icase)
  215.     print("using date: -i flag ignored.\n");
  216.     if (!search) {
  217.     for (n = 0; n < msg_cnt && (!head_first || matches < head_cnt); n++)
  218.         if (msg_bit(list, n))
  219.         ++matches, set_msg_bit(ret_list, n);
  220.     } else
  221.     matches = find_pattern(head_first? head_cnt : msg_cnt,
  222.                pattern, list, ret_list);
  223.     if (xflg && matches >= 0) {
  224.     /* invert items in ret_list that also appear in list */
  225.     bitput(list, ret_list, msg_cnt, ^=);
  226.     /* there should be a faster way to do this count ... */
  227.     for (matches = n = 0; n < msg_cnt; n++)
  228.         if (msg_bit(ret_list, n))
  229.         ++matches;
  230.     }
  231.     Debug("matches = %d\n", matches);
  232.     if (!matches)
  233.     return 0;
  234.  
  235.     /* ok, the list we've got is a list of matched messages.  If "tailing"
  236.      * is set, reduce the number of matches to at least tail_cnt.
  237.      */
  238.     if (tail_cnt >= 0)
  239.     for (n = 0; n < msg_cnt && matches > tail_cnt; n++)
  240.         if (msg_bit(ret_list, n)) {
  241.         Debug("tail: dropping %d\n", n+1);
  242.         unset_msg_bit(ret_list, n);
  243.         matches--;
  244.         }
  245.  
  246.     /* if tailing came before heading, we need to do the heading now. */
  247.     if (!head_first && head_cnt >= 0)
  248.     for (n = 0; n < msg_cnt; n++)
  249.         if (msg_bit(ret_list, n))
  250.         if (head_cnt > 0)
  251.             head_cnt--;
  252.         else {
  253.             unset_msg_bit(ret_list, n);
  254.             matches--;
  255.         }
  256.     return matches;
  257. }
  258.  
  259. /*
  260.  * find_pattern will search thru all the messages set in the check_list
  261.  * until the list runs out or "cnt" has been exhasted.  ret_list contains
  262.  * the list of messages which have matched the pattern.
  263.  * return -1 for internal error or # of pattern matches.
  264.  */
  265. find_pattern(cnt, p, check_list, ret_list)
  266. int cnt;
  267. register char *p;
  268. char check_list[], ret_list[];
  269. {
  270.     register int n, val, i; /* val is return value from regex or re_exec */
  271.     int matches = 0;
  272.     long bytes = 0;
  273.     char buf[HDRSIZ];
  274.     static char *err = (char *)-1;
  275. #ifdef REGCMP
  276.     char *regcmp(), *regex();
  277. #else /* REGCMP */
  278.     char *re_comp();
  279. #endif /* REGCMP */
  280.  
  281.     if (p && *p == '\\')
  282.     p++;  /* take care of escaping special cases (`-', `\') */
  283.  
  284.     /* specify what we're looking for */
  285.     if (p && *p) {
  286.     if (icase)
  287.         p = lcase_strcpy(buf, p);
  288. #ifdef REGCMP
  289.     if (err && p)
  290.         xfree(err);
  291.     if (p && !(err = regcmp(p, NULL))) {
  292.         print("regcmp error: %s\n", p);
  293.         clear_msg_list(ret_list);
  294.         return -1;
  295.     }
  296. #else /* REGCMP */
  297.     if (err = re_comp(p)) {
  298.         print("re_comp error: %s\n", err);
  299.         clear_msg_list(ret_list);
  300.         return -1;
  301.     }
  302. #endif /* REGCMP */
  303.     } else if (err == (char *)-1 && mdy[1] <= 0) {
  304.     print("No previous regular expression\n");
  305.     clear_msg_list(ret_list);  /* doesn't matter really */
  306.     return -1;
  307.     }
  308.     /* start searching: set bytes, and message number: n */
  309.     for (n = 0; cnt && n < msg_cnt; n++)
  310.     if (msg_bit(check_list, n)) {
  311.         if (mdy[1] > 0) {
  312.         int msg_mdy[3];
  313.         if (ison(glob_flags, DATE_RECV))
  314.             p = msg[n].m_date_recv;
  315.         else
  316.             p = msg[n].m_date_sent;
  317.         /* Ick -- fix this mdy thing asap */
  318.         month_day_year(p, &msg_mdy[0], &msg_mdy[1], &msg_mdy[2]);
  319.         Debug("checking %d's date: %d-%d-%d  ",
  320.                  n+1, msg_mdy[0]+1, msg_mdy[1], msg_mdy[2]);
  321.         /* start at year and wrap around.
  322.          * only when match the day (4), check for == (match)
  323.          */
  324.         for (i = 2; i < 5; i++)
  325.             if (before && msg_mdy[i%3] < mdy[i%3]
  326.             ||  after  && msg_mdy[i%3] > mdy[i%3]
  327.             ||  i == 4 && (msg_mdy[i%3] == mdy[i%3])) {
  328.                 Debug("matched (%s).\n",
  329.                 (i == 2)? "year" : (i == 3)? "month" : "day");
  330.                 set_msg_bit(ret_list, n);
  331.                 cnt--, matches++;
  332.                 break;
  333.             } else if (msg_mdy[i%3] != mdy[i%3]) {
  334.             Debug("failed.\n");
  335.             break;
  336.             }
  337.         continue;
  338.         }
  339.         /* we must have the right date -- if we're searching for a
  340.          * string, find it.
  341.          */
  342.         (void) msg_get(n, NULL, 0);
  343.         bytes = 0;
  344.         while (bytes < msg[n].m_size) {
  345.         if (!search_subj && !search_from && !search_to &&
  346.             !*search_hdr && !(p = fgets(buf, sizeof buf, tmpf)))
  347.             break;
  348.         else if (search_subj) {
  349.             if (!(p = header_field(n, "subject")))
  350.             break;
  351.         } else if (search_from) {
  352.             if (!(p = header_field(n, "from"))) {
  353.             /*
  354.              * Check for MSG_SEPARATOR here?  Maybe not...
  355.              */
  356.             register char *p2;
  357.             (void) msg_get(n, NULL, 0);
  358.             if (!(p2 = fgets(buf, sizeof buf, tmpf)) ||
  359.                 !(p = index(p2, ' ')))
  360.                 continue;
  361.             p++;
  362.             if (p2 = any(p, " \t"))
  363.                 *p2 = 0;
  364.             }
  365.         } else if (search_to) {
  366.             if (!(p = header_field(n, "to")) &&
  367.                 !(p = header_field(n, "apparently-to")))
  368.             break;
  369.         } else if (*search_hdr) {
  370.             if (!(p = header_field(n, search_hdr)))
  371.             break;
  372.         }
  373.         if (icase)
  374.             p = lcase_strcpy(buf, p);
  375. #ifdef REGCMP
  376.         val = !!regex(err, p, NULL); /* convert value to a boolean */
  377. #else /* REGCMP */
  378.         val = re_exec(p);
  379. #endif /* REGCMP */
  380.         if (val == -1) {   /* doesn't apply in system V */
  381.             print("Internal error for pattern search.\n");
  382.             clear_msg_list(ret_list); /* it doesn't matter, really */
  383.             return -1;
  384.         }
  385.         if (val) {
  386.             set_msg_bit(ret_list, n);
  387.             cnt--, matches++;
  388.             break;
  389.         }
  390.         if (search_subj || search_from || search_to || *search_hdr)
  391.             break;
  392.         else
  393.             bytes += strlen(p);
  394.         }
  395.     }
  396.     return matches;
  397. }
  398.  
  399. #ifdef CURSES
  400. /*
  401.  * search for a pattern in composed message headers -- also see next function
  402.  * flags ==  0   forward search (prompt).
  403.  * flags == -1   continue search (no prompt).
  404.  * flags ==  1   backward search (prompt).
  405.  */
  406. search(flags)
  407. register int flags;
  408. {
  409.     register char   *p;
  410.     char           pattern[128];
  411.     register int    this_msg = current_msg, val = 0;
  412.     static char     *err = (char *)-1, direction;
  413.     SIGRET        (*oldint)(), (*oldquit)();
  414. #ifdef REGCMP
  415.     char *regcmp();
  416. #else /* REGCMP */
  417.     char *re_comp();
  418. #endif /* REGCMP */
  419.  
  420.     if (msg_cnt <= 1) {
  421.     print("Not enough messages to invoke a search.\n");
  422.     return 0;
  423.     }
  424.     pattern[0] = '\0';
  425.     if (flags == -1)
  426.     print("continue %s search...", direction? "forward" : "backward");
  427.     else
  428.     print("%s search: ", flags? "backward" : "forward");
  429.     if (flags > -1)
  430.     if (Getstr(pattern, COLS-18, 0) < 0)
  431.         return 0;
  432.     else
  433.         direction = !flags;
  434. #ifdef REGCMP
  435.     if (err && *pattern)
  436.     xfree(err);
  437.     else if (err == (char *)-1 && !*pattern) {
  438.     print("No previous regular expression.");
  439.     return 0;
  440.     }
  441.     if (*pattern && !(err = regcmp(pattern, NULL))) {
  442.     print("Error in regcmp in %s", pattern);
  443.     return 0;
  444.     }
  445. #else /* REGCMP */
  446.     if (err = re_comp(pattern)) {
  447.     print(err);
  448.     return 0;
  449.     }
  450. #endif /* REGCMP */
  451.     move(LINES-1, 0), refresh();
  452.     on_intr();
  453.  
  454.     do  {
  455.     if (direction)
  456.         current_msg = (current_msg+1) % msg_cnt;
  457.     else
  458.         if (--current_msg < 0)
  459.         current_msg = msg_cnt-1;
  460.     p = compose_hdr(current_msg);
  461. #ifdef REGCMP
  462.     val = !!regex(err, p, NULL); /* convert value to a boolean */
  463. #else /* REGCMP */
  464.     val = re_exec(p);
  465. #endif /* REGCMP */
  466.     if (val == -1)     /* doesn't apply in system V */
  467.         print("Internal error for pattern search.\n");
  468.     } while (!val && current_msg != this_msg && isoff(glob_flags, WAS_INTR));
  469.  
  470.     if (ison(glob_flags, WAS_INTR)) {
  471.     print("Pattern search interrupted.");
  472.     current_msg = this_msg;
  473.     } else if (val == 0)
  474.     print("Pattern not found.");
  475.  
  476.     off_intr();
  477.     return val;
  478. }
  479. #endif /* CURSES */
  480.  
  481. /*
  482.  * Get just the month, day, and year from a date.
  483.  * This is a temporary measure until the date compares in pick()
  484.  * can be overhauled.  It really should be in dates.c, but ...
  485.  */
  486. static
  487. void
  488. month_day_year(date, month, day, year)
  489. char *date;
  490. int *month, *day, *year;
  491. {
  492.     long gmt;
  493.     char unused[4], zone[8];
  494.     struct tm *t;
  495.     extern long getzoff();
  496.  
  497.     (void) sscanf(date, "%ld%3c%s", &gmt, unused, zone);
  498.     gmt += getzoff(zone);
  499.     t = gmtime(&gmt);
  500.     *month = t->tm_mon;
  501.     *day = t->tm_mday;
  502.     *year = t->tm_year;
  503. }
  504.  
  505. /*
  506.  * parse a user given date string and set mdy[] array with correct
  507.  * values.  Return 0 on failure.
  508.  */
  509. date1(p)
  510. register char *p;
  511. {
  512.     register char *p2;
  513.     long      t;
  514.     int       i;
  515.     struct tm       *today;
  516.  
  517.     if (*p == '-' || *p == '+') {
  518.     before = !(after = *p == '+');
  519.     skipspaces(1);
  520.     }
  521.     if (!isdigit(*p) && *p != '/') {
  522.     print("syntax error on date: \"%s\"\n", p);
  523.     return 0;
  524.     }
  525.     (void) time (&t);
  526.     today = localtime(&t);
  527.     for (i = 0; i < 3; i++)
  528.     if (!p || !*p || *p == '/') {
  529.         switch(i) {   /* default to today's date */
  530.         case 0: mdy[0] = today->tm_mon;
  531.         when 1: mdy[1] = today->tm_mday;
  532.         when 2: mdy[2] = today->tm_year;
  533.         }
  534.         if (p && *p)
  535.         p++;
  536.     } else {
  537.         p2 = (*p)? index(p+1, '/') : NULL;
  538.         mdy[i] = atoi(p); /* atoi will stop at the '/' */
  539.         if (i == 0 && (--(mdy[0]) < 0 || mdy[0] > 11)) {
  540.         print("Invalid month: %s\n", p);
  541.         return 0;
  542.         } else if (i == 1 && (mdy[1] < 1 || mdy[1] > 31)) {
  543.         print("Invalid day: %s\n", p);
  544.         return 0;
  545.         }
  546.         if (p = p2) /* set p to p2 and check to see if it's valid */
  547.         p++;
  548.     }
  549.     return 1;
  550. }
  551.  
  552. /*
  553.  * Parse arguments specifying days/months/years "ago" (relative to today).
  554.  * Legal syntax: -ago [+-][args]
  555.  *    where "args" is defined to be:
  556.  *    [0-9]+[ ]*[dD][a-Z]*[ ,]*[0-9]+[mM][a-Z]*[ ,]*[0-9]+[ ]*[yY][a-Z]*
  557.  *    1 or more digits, 0 or more spaces, d or D followed by 0 or more chars,
  558.  *    0 or more whitespaces or commas, repeat for months and years...
  559.  * Examples:
  560.  *    1 day, 2 months, 0 years
  561.  *    2 weeks 1 year
  562.  *    10d, 5m
  563.  *    3w
  564.  *    1d 1Y
  565.  *
  566.  * Return number of args parsed; -1 on error.
  567.  */
  568. ago_date(argv)
  569. char **argv;
  570. {
  571. #define SECS_PER_DAY   (60 * 60 * 24)
  572. #define SECS_PER_WEEK  (SECS_PER_DAY * 7)
  573. #define SECS_PER_MONTH ((int)(SECS_PER_DAY * 30.5))
  574. #define SECS_PER_YEAR  (SECS_PER_DAY * 365)
  575.     register char *p;
  576.     char       buf[256];
  577.     int           n = 0, value;
  578.     long       t;
  579.     struct tm       *today;
  580.  
  581.     (void) argv_to_string(buf, argv);
  582.     p = buf;
  583.     (void) time (&t); /* get current time in seconds and subtract new values */
  584.     if (*p == '-')
  585.     before = TRUE;
  586.     else if (*p == '+')
  587.     after = TRUE;
  588.     skipspaces(before || after);
  589.     while (*p) {
  590.     if (!isdigit(*p)) {
  591.         p -= 2;
  592.         break; /* really a syntax error, but it could be other pick args */
  593.     }
  594.     p = my_atoi(p, &value); /* get 1 or more digits */
  595.     skipspaces(0); /* 0 or more spaces */
  596.     switch (lower(*p)) {   /* d, m, or y */
  597.         case 'd' : t -= value * SECS_PER_DAY;
  598.         when 'w' : t -= value * SECS_PER_WEEK;
  599.         when 'm' : t -= value * SECS_PER_MONTH;
  600.         when 'y' : t -= value * SECS_PER_YEAR;
  601.         otherwise: return -1;
  602.     }
  603.     for (p++; Lower(*p) >= 'a' && *p <= 'z'; p++)
  604.         ; /* skip the rest of this token */
  605.     while (*p == ',' || isspace(*p))
  606.         ++p; /* 0 or more whitespaces or commas */
  607.     }
  608.     today = localtime(&t);
  609.     mdy[0] = today->tm_mon;
  610.     mdy[1] = today->tm_mday;
  611.     mdy[2] = today->tm_year;
  612.  
  613.     /* Count the number of args parsed */
  614.     for (n = 0; p > buf && *argv; n++)
  615.     p -= (strlen(*argv++)+1);
  616.     Debug("parsed %d args\n", n);
  617.     return n;
  618. }
  619.